﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Web;

namespace InsertFromSkyDrive
{
    public partial class frmBrowse : Form
    {

        #region Global Variables
        string skydriveurl = "http://cid-{0}.skydrive.live.com/";
        string skydrivehome = "https://cid-{0}.skydrive.live.com/home.aspx";
        string m_cid = "";
        string user = "";
        public string Url = "";

        TreeNode root;
        TreeNode title;
        Regex r;
        BackgroundWorker getFolders;
        #endregion

        public frmBrowse(string cid)
        {
            m_cid = cid;
            InitializeComponent();
            getFolders = new BackgroundWorker();
            getFolders.WorkerReportsProgress = false;
            getFolders.WorkerSupportsCancellation = false;
            getFolders.RunWorkerCompleted += new RunWorkerCompletedEventHandler(getFolders_RunWorkerCompleted);
            getFolders.DoWork += new DoWorkEventHandler(getFolders_DoWork);

            // Adds a root to the treeview
            root = treeView1.Nodes.Add("SkyDrive");
            root.Name = "http://skydrive.live.com/";

            getFolders.RunWorkerAsync();
        }

        void getFolders_DoWork(object sender, DoWorkEventArgs e)
        {
            Invoke(new ShowHide(ShowHideSpinner), true);
            string link = string.Format(skydrivehome, m_cid);
            string t = GetSource(link);
            List<string> a = new List<string>();

            // this is a regular expression to check for the urls 
            r = new Regex("href\\s*=\\s*(?:(?:\\\"(?<url>[^\\\"]*)\\\")|(?<url>[^\\s]* ))");
            // get all the matches depending upon the regular expression
            MatchCollection mcl = r.Matches(t);

            // In the source code is a span tag that has the user's display name, this regex gets that
            Match m = Regex.Match(t, "<span id=\"sdx_crd_name\">(.*?)</span>");
            if (m.Success)
                user = HttpUtility.HtmlDecode(m.Groups[1].Value); // Gets rid of any crap in the name

            foreach (Match ml in mcl)
            {
                // This loop basically gets all the folder links that are at the root level of the user's SkyDrive account
                foreach (Group g in ml.Groups)
                {
                    string b = g.Value + "";

                    // Checks the links are publicly available, if they are, they will start http, not https
                    // Add the extracted urls to the array list
                    if (b.StartsWith("http://cid-"))
                        a.Add(b);
                }
            }
            // See if there are any public folders.
            if (a.Count == 0)
            {
                MessageBox.Show("Sorry, you do not have any public folders to show", "Doh", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                // Create the node that has the user's name in it, all public folders will appear under this node.
                title = new TreeNode(user);
                title.Name = link;

                // Add the node to the root ("SkyDrive")
                Invoke(new AddToTree(AddToTreeFunction), title, root);
                Invoke(new ChangeAdding(ChangeAddingFile), "user: " + user);
                string p = "";

                // Now we cycle through the list of public folders and add all sub folders and files to the tree
                foreach (string n in a)
                {
                    p = HttpUtility.UrlDecode(n.Substring(n.IndexOf("browse.aspx") + 12), Encoding.ASCII);

                    // Creates the node for the top most folders
                    TreeNode node = new TreeNode(p);
                    node.Name = n;
                    node.ToolTipText = p;

                    // Adds the new node to the user's node
                    Invoke(new AddToTree(AddToTreeFunction), node, title);
                    Invoke(new ChangeAdding(ChangeAddingFile), "folder: " + p);

                    // Sends across the newly created node, plus the folder's link to get all subfolders and files added.
                    AddNode(n, node);

                }
                Invoke(new Expand(ExpandTrees));
            }
        }

        void getFolders_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Invoke(new ShowHide(ShowHideSpinner), false);
        }

        /// <summary>
        /// Adds a new node.
        /// </summary>
        /// <param name="link">The link.</param>
        /// <param name="parentnode">The parentnode.</param>
        private void AddNode(string link, TreeNode parentnode)
        {
            // Set the root skydrive link for that user
            string skydrive = string.Format(skydriveurl, m_cid);
            string name = "";

            bool browse = false;

            // Get the source for the link we've been passed
            string source = GetSource(link);

            // Decode the link to get rid of the crap that might be there
            link = HttpUtility.UrlDecode(link, Encoding.ASCII);

            // Set the name to be what ever appears after the browse part of the link,
            // this will be used to get just the file or folder name of the new entry
            name = link.Substring(link.IndexOf("browse.aspx") + 12);

            bool filedone = false;

            // Get a list of all the links in that link's source code
            MatchCollection mc = r.Matches(source);
            foreach (Match ml in mc)
            {
                foreach (Group g in ml.Groups)
                {
                    // Decode the link, gets rid of any crap in it.
                    string b = HttpUtility.UrlDecode(g.Value + "", Encoding.ASCII);
                    // Only links starting with http://cid- are links that we are interested in
                    // links starting with https://cid- aren't public folders or files, so we want to
                    // ignore them.

                    if (b.Contains("livefilestore.com") && !filedone)
                    {
                        if (b.Contains("?download"))
                            parentnode.Name = b.Substring(6, b.IndexOf("?download") + 2);
                        else
                            parentnode.Name = b.Substring(6, b.Length - 7);
                        filedone = true;
                    }
                    else if (b.StartsWith("http://cid-") && link.Contains("browse.aspx"))
                    {
                        // This if statement checks for particular types of links. In the source, we will
                        // sometimes get a self.aspx page that points to the folder we're already browsing
                        // so we don't want to be adding that.
                        if ((b.Contains("self.aspx") || b.Contains("browse.aspx")) && // makes sure the link is either a folder or file link
                            (b != string.Format("{0}self.aspx/{1}", skydrive, name) && // Checks that the link is not a self.aspx link to the folder we're browsing
                            b != string.Format("{0}self.aspx/{1}/", skydrive, name)) && // Checks the same again but with a trailing slash
                            (b != link || b != link + "/") && !b.EndsWith("/") && // Checks that the new link is not the same as the original link passed to the function
                            !b.EndsWith(parentnode.Name) && // Checks that the new link doesn't end with the name of the parent node, in other words, its the effectively the same link
                            !link.Contains(b)) // Checks that the new link is not already contained in the original link, if it is, we don't want it.
                        {

                            string file = "";
                            TreeNode child = new TreeNode();
                            if (b.Contains("self.aspx")) // Sees if it's a file
                            {
                                // Get the file name
                                file = b.Substring(b.IndexOf("self.aspx") + 10 + name.Length + 1);
                                Invoke(new ChangeAdding(ChangeAddingFile), "file: " + file);

                            }
                            if (b.Contains("browse.aspx")) // Sees if it's a folder
                            {
                                // Get the folder name
                                file = b.Substring(b.IndexOf("browse.aspx") + 13 + name.Length);
                                Invoke(new ChangeAdding(ChangeAddingFile), "folder: " + file);
                                browse = true;

                            }
                            child.Text = file; // Sets the Treenode's text
                            child.Name = b; // Sets the Treenode's name

                            // Adds the child node to the parent node that got passed to the function
                            Invoke(new AddToTree(AddToTreeFunction), child, parentnode);
                            //parentnode.Nodes.Add(child);

                            // Checks whether the link was a folder and if so, we need to recurse through the link
                            // by passing the new link (b) back into this function with the new child node as the parent node
                            if (browse)
                                AddNode(b, child);

                        } // End big ass if statement
                    } // end if http://cid check
                } // End foreach
            } // End foreach
        }

        // Delegate and function for adding a node into the tree from the backgroundworker        
        private delegate void AddToTree(TreeNode node, TreeNode parentnode);
        private void AddToTreeFunction(TreeNode node, TreeNode parentnode)
        {
            parentnode.Nodes.Add(node);
        }

        // Delegate and function for expanding the tree nodes so that the public folders are listed.
        private delegate void Expand();
        private void ExpandTrees()
        {
            root.Expand();
            title.Expand();
        }

        // Delegate and function for showing or hiding the spinner to indicate the program is doing something.
        private delegate void ShowHide(bool yn);
        private void ShowHideSpinner(bool yn)
        {
            pictureBox1.Visible = yn;
            lblAdding.Visible = yn;
        }

        private delegate void ChangeAdding(string filefolder);
        private void ChangeAddingFile(string filefolder)
        {
            lblAdding.Visible = true;
            lblAdding.Text = "Adding " + filefolder;
        }

        /// <summary>
        /// Gets the source code of the given link.
        /// </summary>
        /// <param name="link">The link.</param>
        /// <returns></returns>
        private string GetSource(string link)
        {

            string t = "";
            try
            {
                System.IO.Stream st = null;
                System.IO.StreamReader sr = null;

                // make a Web request
                System.Net.WebRequest req = System.Net.WebRequest.Create(link);

                // get the response and read from the result stream
                System.Net.WebResponse resp = req.GetResponse();
                st = resp.GetResponseStream();
                sr = new System.IO.StreamReader(st);

                // read all the text in it
                t = sr.ReadToEnd();
            }
            catch (Exception ex)
            {
                MessageBox.Show("There was an error: " + ex.Message, "Big Fat Hairy Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return t;
        }

        private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            treeView1.SelectedNode = e.Node;
            if (CheckNode())
                btnSelect.Enabled = true;
        }

        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.Nodes.Count == 0)
            {
                treeView1_NodeMouseClick(this, e);
                if (CheckNode())
                {
                    btnSelect.Enabled = true;
                    btnSelect_Click(sender, null);
                }
            }
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            treeView1.SelectedNode = e.Node;
            if (CheckNode())
                btnSelect.Enabled = true;
        }

        private void btnSelect_Click(object sender, EventArgs e)
        {
            Url = treeView1.SelectedNode.Name;
            this.DialogResult = DialogResult.OK;
            this.Close();
        }

        private bool CheckNode()
        {
            if (treeView1.SelectedNode.Text == "SkyDrive" || treeView1.SelectedNode.Parent.Text == "SkyDrive")
                return false;
            else
                return true;
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            if (getFolders.IsBusy)
            {
                MessageBox.Show("There is a refresh already in progress", "Doh", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            root.Nodes.Clear();
            getFolders.RunWorkerAsync();
        }
    }
}
